/*
 * abstract class for different pricers
 */

#include <iostream>
#include <cmath>
#include <stdexcept>
#include <utility>
#include "pricer.hpp"
#include "../statistics/inverse_normal_distribution.hpp"
#include "../statistics/random_generator.hpp"
using namespace std;

pricer::pricer(product_type _product, pricer_type _pricer) : theType(_product), thePricer(_pricer){};

pair<double,double> pricer::evaluate (double _strikePrice, double _time, double _expectedRate, double _volatility, double _confidence )
{
		switch(thePricer)
		{
			case MONTE_CARLO_PRICER:
			{
				return evaluateMC (_strikePrice, _time, _expectedRate, _volatility, _confidence );
				break;
			}
			case BHM_ZERO_RISK_PRICER:
			{
				return evaluateBHM (_strikePrice, _time, _expectedRate, _volatility, _confidence );
				break;
			}
			default:
				cout<<"undefined option in the evaluate function"<<endl;
		}
		return make_pair(0.0, 0.0);
}
	/*
	 * temp fix const for now
	 */
	const long MC_COUNT = 500;
	const double strike = 0.0;

pair<double,double> pricer::evaluateMC (double _strikePrice, double _time, double _expectedRate, double _volatility, double _confidence )
{
		double variance = _volatility*_volatility*_time;
		double spotT = _strikePrice*exp(_expectedRate*_time-0.5*variance);
		double sqrt_var = sqrt(variance);
		double sum = 0.0;

		NormalRandomNumber nrn;
		for (long i=0;i<MC_COUNT; ++i)
		{
			double nextRandom = nrn.next();
			double spotTT = spotT*exp(sqrt_var);
			double pay = spotTT - strike;
			pay = pay>0? pay:0;
			sum+=pay;
		}

		double meanT = sum/ MC_COUNT* exp(-_expectedRate*_time);
		return make_pair(meanT, 0.0);
}


pair<double,double> pricer::evaluateBHM (double _strikePrice, double _time, double _expectedRate, double _volatility, double _confidence )
{

	try
	{
		double meanT = log(_strikePrice) + (_expectedRate - _volatility*_volatility/2.0)*_time;
		double rmsT = _volatility*sqrt(_time);
		ConfidenceInterval confidenceInt(meanT, rmsT, _confidence);
		double rMargin = exp(confidenceInt.getRightMargin());
		double lMargin = exp(confidenceInt.getLeftMargin());
		return make_pair(lMargin, rMargin);
	} catch( invalid_argument &exc ) {
		cout << "error in pricer::evaluate: " << exc.what() << endl;
	}
	return make_pair(0.0, 0.0);
}
